import re
import sys
from random import randint

import requests

import base64
import time
from urllib.parse import quote

def generate_cipher_text(password):
    """生成加密后的密码和时间戳"""
    t0 = int(time.time() * 1000)  # 当前时间戳(毫秒)
    p_bytes = password.encode('utf-8')
    v = (t0 >> 16) & 0xFF
    
    for b in p_bytes:
        v ^= b
        
    remainder = v % 100
    t1 = (t0 // 100) * 100 + remainder
    
    # 将密码转换为16进制字符串
    p1 = ''.join([f'{b:02x}' for b in p_bytes])
    s = f'{t1}*{p1}'
    
    # Base64编码
    encrypted = base64.b64encode(s.encode('utf-8')).decode('utf-8')
    return encrypted, t1

# 查询课程信息部分的修改
def get_course_list(session):
    url = "https://welearn.sflep.com/ajax/authCourse.aspx"
    
    headers = {
        'host': 'welearn.sflep.com',
        'sec-ch-ua-platform': '"Windows"',
        'x-requested-with': 'XMLHttpRequest',
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 Edg/132.0.0.0',
        'accept': 'application/json, text/javascript, */*; q=0.01',
        'sec-ch-ua': '"Not A(Brand";v="8", "Chromium";v="132", "Microsoft Edge";v="132"',
        'sec-ch-ua-mobile': '?0',
        'sec-fetch-site': 'same-origin',
        'sec-fetch-mode': 'cors',
        'sec-fetch-dest': 'empty',
        'referer': 'https://welearn.sflep.com/student/index.aspx',
        'Cookie': session.cookies_str
    }
    
    params = {
        'action': 'gmc'
    }
    
    try:
        response = session.get(url, params=params, headers=headers)
        # 尝试解析JSON响应
        try:
            json_data = response.json()
            return json_data
        except ValueError:
            # 如果不是JSON，检查是否是重定向脚本
            if '<script' in response.text:
                print("登录状态已失效，需要重新登录")
            return None
            
    except Exception as e:
        print("请求发生错误:", str(e))
        return None


session = requests.Session()
def printline():
    print('-'*51)
# 获取账户密码
try:  # 直接从命令行中获取
    username, password = sys.argv[1], sys.argv[2]
except:
    print('=================WELearn自动刷课脚本===============')
    print('===========有问题请邮件联系wx:djxxpt2020===========')
    printline()
    print('开源地址: https://gitee.com/xxxhhy/welearn-curriculum-finsh')
    printline()
    print('==================请选择登录方式==================')
    loginmode=input('请选择登录方式: \n  1.账号密码登录 \n  2.Cookie登录\n\n请输入数字1或2: ')
    printline()
    if loginmode=='1':
        username = input('请输入账号: ')
        password = input('请输入密码: ')
        
        # 第1步：SSO登录
        encrypted_pwd, timestamp = generate_cipher_text(password)
        
        login_data = {
            'rturl': '/connect/authorize/callback?client_id=welearn_web&redirect_uri=https%3A%2F%2Fwelearn.sflep.com%2Fsignin-sflep&response_type=code&scope=openid%20profile%20email%20phone%20address&code_challenge=p18_2UckWpdGfknVKQp6Ang64zAYH6__0Z8eQu2uuZE&code_challenge_method=S256&state=OpenIdConnect.AuthenticationProperties%3DBhc1Qn6lYFZrxO_KhC7UzXZTYACtsAnIVT0PgzDlhtuxIXeSFLwXaNbthEeuwSCbzvhrw2wECCxFTq8tbd7k2OFPfH0_TCnMkuh8oBFmlhEsZ3ZXUYecidfT2h2YpAyAoaBaXfpuQj2SGCIEW3KVRYpnljmx-mso97xCbjz72URywiBJRMqDS9TqY-0vaviUIH1X72u_phfuiBdbR1s-WOyUj21KAPdNPJXi1nQtUd-hRoeI53WBTrv2EC0U4SNFvhivPgE6YseB2fdYbPv4u0NiFeHPD3EBQyqE_iUVI1QrGPG3VvhD5xs8odx21WncybewKIuTQpH3MAfJkTmDeQ&x-client-SKU=ID_NET472&x-client-ver=6.32.1.0',
            'account': username,
            'pwd': encrypted_pwd,
            'ts': str(timestamp)
        }
        
        login_headers = {
            'host': 'sso.sflep.com',
            'sec-ch-ua-platform': '"Windows"',
            'x-requested-with': 'XMLHttpRequest',
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 Edg/132.0.0.0',
            'content-type': 'application/x-www-form-urlencoded',
            'accept': 'application/json, text/plain, */*',
            'origin': 'https://sso.sflep.com',
            'referer': 'https://sso.sflep.com/idsvr/login.html'
        }
        
        res = session.post(
            "https://sso.sflep.com/idsvr/account/login", 
            data=login_data,
            headers=login_headers
        )
        
        if res.ok and 'code' in res.json() and res.json()['code'] == 0:
            print("登录成功!!")
            
            # 第2步：处理SSO回调
            callback_url = "https://sso.sflep.com/idsvr/connect/authorize/callback"
            callback_params = {
                'client_id': 'welearn_web',
                'redirect_uri': 'https://welearn.sflep.com/signin-sflep',
                'response_type': 'code',
                'scope': 'openid profile email phone address',
                'code_challenge': 'p18_2UckWpdGfknVKQp6Ang64zAYH6__0Z8eQu2uuZE',
                'code_challenge_method': 'S256',
                'state': 'OpenIdConnect.AuthenticationProperties=Bhc1Qn6lYFZrxO_KhC7UzXZTYACtsAnIVT0PgzDlhtuxIXeSFLwXaNbthEeuwSCbzvhrw2wECCxFTq8tbd7k2OFPfH0_TCnMkuh8oBFmlhEsZ3ZXUYecidfT2h2YpAyAoaBaXfpuQj2SGCIEW3KVRYpnljmx-mso97xCbjz72URywiBJRMqDS9TqY-0vaviUIH1X72u_phfuiBdbR1s-WOyUj21KAPdNPJXi1nQtUd-hRoeI53WBTrv2EC0U4SNFvhivPgE6YseB2fdYbPv4u0NiFeHPD3EBQyqE_iUVI1QrGPG3VvhD5xs8odx21WncybewKIuTQpH3MAfJkTmDeQ'
            }
            
            # 跟随重定向处理SSO回调
            response = session.get(callback_url, params=callback_params, allow_redirects=True)
            
            # 获取并保存最终的cookies
            cookies_list = []
            for cookie in session.cookies:
                cookies_list.append(f"{cookie.name}={cookie.value}")
            session.cookies_str = "; ".join(cookies_list)
        
        else:
            print("登录失败:", res.text)
            input("按任意键退出")
            exit(0)
    elif loginmode=='2':
        try:
            cookie_str = input('请粘贴Cookie: ').strip()
            
            # 保存完整的cookies字符串
            session.cookies_str = cookie_str
            
            # 测试登录状态
            course_data = get_course_list(session)
            if course_data and 'clist' in course_data:
                print('登录成功!!!')
                print('课程列表:', course_data['clist'])
            else:
                print('登录失败!!!')
                input("按任意键退出")
                exit(0)
                
        except Exception as e:
            print('Cookie处理错误:', str(e))
            input('Cookie输入错误!!!')
            exit(0)
    else:
        input('输入错误!!')
        exit(0) 
printline()

while True:
    # 查询课程信息
    response = get_course_list(session)
    
    try:
        # response已经是字典类型，直接检查内容
        if not response or 'clist' not in response:
            print('发生错误!!!可能是登录错误或没有课程!!!')
            input("按任意键退出")
            exit(0)
            
        back = response["clist"]
        print('查询课程成功!!!')
        printline()
        print('我的课程: \n')
        
        # 显示课程列表
        for i, course in enumerate(back, 1):
            print(f'[NO.{i:>2d}]  {course["name"]}')
            
    except Exception as e:
        print("处理课程数据时发生错误:", str(e))
        input("按任意键退出")
        exit(0)

    # 选择课程
    order = int(input("\n请输入需要完成的课程序号（上方[]内的数字）: "))
    cid = back[order - 1]["cid"]
    printline()
    print("获取单元中...")
    printline()
    # 刷课模块
    url = f"https://welearn.sflep.com/student/course_info.aspx?cid={cid}"
    response = session.get(url)

    uid = re.search('"uid":(.*?),', response.text).group(1)
    classid = re.search('"classid":"(.*?)"', response.text).group(1)

    url = 'https://welearn.sflep.com/ajax/StudyStat.aspx'
    response = session.get(url,params={'action':'courseunits','cid':cid,'uid':uid},headers={'Referer':'https://welearn.sflep.com/student/course_info.aspx'})
    back = response.json()['info']

    # 选择单元 使用了WELearnToSleeep的代码
    print('[NO. 0]  按顺序完成全部单元课程')
    unitsnum = len(back)
    for i,x in enumerate(back,start=1):
        if x['visible']=='true':
            print(f'[NO.{i:>2d}]  [已开放]  {x["unitname"]}  {x["name"]}')
        else:
            print(f'[NO.{i:>2d}] ![未开放]! {x["unitname"]}  {x["name"]}')
    unitidx = int(input('\n\n请选择需要完成的单元序号（上方[]内的数字，输入0为按顺序刷全部单元）： '))
    printline()
    inputcrate = input('模式1:每个练习定正确率，请直接输入指定的正确率\n如:希望每个练习正确率均为100，则输入 100\n\n式2:每个练习随机正确率，请输入正确率上下限并用英文逗号隔开\n如:希望每个练习正确率为70～100，则输入 70,100\n\n请严格按照以上格式输入每个练习的正确率: ')
    if ',' in inputcrate:
        mycrate=eval(inputcrate)
        randommode=True
    else:
        mycrate=inputcrate
        randommode=False
    printline()
    # 伪造请求
    way1Succeed, way2Succeed, way1Failed, way2Failed = 0, 0, 0, 0

    ajaxUrl = "https://welearn.sflep.com/Ajax/SCO.aspx"
    infoHeaders = {
        "Referer": f"https://welearn.sflep.com/student/course_info.aspx?cid={cid}",
    }

    if(unitidx == 0):
        i = 0
    else:
        i = unitidx - 1
        unitsnum = unitidx

    while True:
        response = session.get(
            f'https://welearn.sflep.com/ajax/StudyStat.aspx?action=scoLeaves&cid={cid}&uid={uid}&unitidx={i}&classid={classid}', headers=infoHeaders)

        if "异常" in response.text or "出错了" in response.text:
            break

        for course in response.json()["info"]:
            if course['isvisible']=='false':  # 跳过未开放课程
                print(f'[!!跳过!!]    {course["location"]}')
            elif "未" in course["iscomplete"]:  # 章节未完成
                print(f'[即将完成]    {course["location"]}')
                if randommode is True:
                    crate=str(randint(mycrate[0],mycrate[1]))
                else:
                    crate=mycrate
                data = '{"cmi":{"completion_status":"completed","interactions":[],"launch_data":"","progress_measure":"1","score":{"scaled":"'+crate+'","raw":"100"},"session_time":"0","success_status":"unknown","total_time":"0","mode":"normal"},"adl":{"data":[]},"cci":{"data":[],"service":{"dictionary":{"headword":"","short_cuts":""},"new_words":[],"notes":[],"writing_marking":[],"record":{"files":[]},"play":{"offline_media_id":"9999"}},"retry_count":"0","submit_time":""}}[INTERACTIONINFO]'

                id = course["id"]
                session.post(ajaxUrl, data={"action": "startsco160928",
                                            "cid": cid,
                                            "scoid": id,
                                            "uid": uid
                                            },
                             headers={"Referer": f"https://welearn.sflep.com/Student/StudyCourse.aspx?cid={cid}&classid={classid}&sco={id}"})
                response = session.post(ajaxUrl, data={"action": "setscoinfo",
                                                       "cid": cid,
                                                       "scoid": id,
                                                       "uid": uid,
                                                       "data": data,
                                                       "isend": "False" },
                                        headers={"Referer": f"https://welearn.sflep.com/Student/StudyCourse.aspx?cid={cid}&classid={classid}&sco={id}"})
                print(f'>>>>>>>>>>>>>>正确率:{crate:>3}%',end='  ')
                if '"ret":0' in response.text:
                    print("方式1:成功!!!", end="  ")
                    way1Succeed += 1
                else:
                    print("方式1:失败!!!", end="  ")
                    way1Failed += 1

                response = session.post(ajaxUrl, data={"action": "savescoinfo160928",
                                                       "cid": cid,
                                                       "scoid": id,
                                                       "uid": uid,
                                                       "progress": "100",
                                                       "crate": crate,
                                                       "status": "unknown",
                                                       "cstatus": "completed",
                                                       "trycount": "0",
                                                       },
                                        headers={"Referer": f"https://welearn.sflep.com/Student/StudyCourse.aspx?cid={cid}&classid={classid}&sco={id}"})
#                sleep(1) # 延迟1秒防止服务器压力过大
                if '"ret":0' in response.text:
                    print("方式2:成功!!!")
                    way2Succeed += 1
                else:
                    print("方式2:失败!!!")
                    way2Failed += 1
            else:  # 章节已完成
                print(f'[ 已完成 ]    {course["location"]}')

        if unitidx != 0:
            break
        else:
            i += 1
    if unitidx == 0:
        break
    else:
        print('本单元运行完毕！回到选课处！！\n\n\n\n')
        printline()

printline()
print(f"""
***************************************************
全部完成!!

总计:
方式1: {way1Succeed} 成功, {way1Failed} 失败
方式2: {way2Succeed} 成功, {way2Failed} ��败

**********  有问题请邮件联系wx:djxxpt2020  **********""")
input("按任意键退出")

